/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  							 script_action_type.cpp

	$Header: /heroes4/script_action_type.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "script_action_type.h"

#include <bitset>
#include <cassert>
#include <cstdlib>
#include <map>
#include <utility>

#include "elements_of.h"
#include "enum_operations.h"
#include "script_context.h"
#include "string_insensitive_compare.h"

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::bitset< k_script_context_count > t_script_context_mask;

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_script_context_mask build_script_context_mask(
		t_script_context const *	contexts,
		size_t						count )
	{
		t_script_context_mask result;

		while ( count > 0 )
		{
			t_script_context context = *contexts++;
			assert( context >= 0 && context < k_script_context_count );
			assert( !result.test( context ) );

			result.set( context );

			--count;
		}

		return result;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::string t_keyword_table[ k_script_action_type_count ];
	t_keyword_table const & get_keyword_table()
	{
		static std::string const k_keyword_table[ k_script_action_type_count ] =
		{
			"build",
			"change_owner",
			"clear_lc_text",
			"clear_l_msg",
			"clear_vc_text",
			"clear_v_msg",
			"combat",
			"if",
			"dec_mana",
			"dec_damage",
			"dec_luck",
			"dec_max_hp",
			"dec_max_mana",
			"dec_morale",
			"dec_pop",
			"dec_speed",
			"detonate",
			"disable_vc",
			"text",
			"enable_vc",
			"give_artifact",
			"give_creature",
			"give_material",
			"give_skill",
			"give_spell",
			"inc_move",
			"inc_mana",
			"inc_damage",
			"inc_exp",
			"inc_level",
			"inc_luck",
			"inc_max_hp",
			"inc_max_mana",
			"inc_morale",
			"inc_pop",
			"inc_skill",
			"inc_speed",
			"no_op",
			"lose",
			"ask",
			"rem_event",
			"rem_this",
			"seq",
			"set_bool",
			"set_lc_text",
			"set_l_msg",
			"set_num",
			"set_vc_text",
			"set_v_msg",
			"rem_artifact",
			"take_creature",
			"take_material",
			"gosub",
			"win",
		};

		return k_keyword_table;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::map< std::string, t_script_action_type, t_string_insensitive_less > t_type_map;
	t_type_map const & get_type_map()
	{
		static t_type_map type_map;
		static bool initialized = false;
		if ( !initialized )
		{
			t_keyword_table const & k_keyword_table = get_keyword_table();

			t_script_action_type type;
			for (	type = t_script_action_type( 0 );
					type < k_script_action_type_count;
					enum_incr( type ) )
				type_map.insert( std::make_pair( k_keyword_table[ type ], type ) );

			initialized = true;
		}

		return type_map;
	}

} // Unnamed namespace

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
std::string const & get_keyword( t_script_action_type type )
{
	assert( type >= 0 && type < k_script_action_type_count );

	static t_keyword_table const & k_keyword_table = get_keyword_table();
	return k_keyword_table[ type ];
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_script_action_type get_script_action_type( std::string const & keyword )
{
	static t_type_map const & k_type_map = get_type_map();

	t_type_map::const_iterator iter = k_type_map.find( keyword );
	if ( iter == k_type_map.end() )
		return k_script_action_none;

	return iter->second;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool available( t_script_action_type action_type, t_script_context context )
{
	assert( action_type >= 0 && action_type < k_script_action_type_count );
	assert( context >= 0 && context < k_script_context_count );

	switch ( action_type )
	{
	case k_script_action_decrease_luck:
	case k_script_action_decrease_morale:
	case k_script_action_give_artifacts:
	case k_script_action_increase_luck:
	case k_script_action_increase_morale:
	case k_script_action_take_artifacts:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_hero,
				k_script_context_hero_in_combat,
				k_script_context_town,
				k_script_context_town_in_combat,
				k_script_context_town_with_visitor,
				k_script_context_placed_event,
				k_script_context_seers_hut,
				k_script_context_quest_guard,
				k_script_context_quest_gate,
				k_script_context_army,
				k_script_context_army_in_combat,
				k_script_context_garrison,
				k_script_context_garrison_in_combat,
				k_script_context_garrison_with_visitor,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}

	case k_script_action_decrease_cur_spell_points:
	case k_script_action_decrease_damage:
	case k_script_action_decrease_max_hit_points:
	case k_script_action_decrease_max_spell_points:
	case k_script_action_decrease_speed:
	case k_script_action_give_skill:
	case k_script_action_give_spell:
	case k_script_action_increase_cur_spell_points:
	case k_script_action_increase_damage:
	case k_script_action_increase_experience:
	case k_script_action_increase_experience_level:
	case k_script_action_increase_max_hit_points:
	case k_script_action_increase_max_spell_points:
	case k_script_action_increase_skill:
	case k_script_action_increase_speed:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_hero,
				k_script_context_hero_in_combat,
				k_script_context_town,
				k_script_context_town_in_combat,
				k_script_context_town_with_visitor,
				k_script_context_placed_event,
				k_script_context_seers_hut,
				k_script_context_quest_guard,
				k_script_context_quest_gate,
				k_script_context_army,
				k_script_context_army_in_combat,
				k_script_context_garrison,
				k_script_context_garrison_in_combat,
				k_script_context_garrison_with_visitor,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}

	case k_script_action_build_building:
	case k_script_action_decrease_population:
	case k_script_action_increase_population:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_town,
				k_script_context_town_in_combat,
				k_script_context_town_with_visitor,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}

	case k_script_action_change_owner:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_town,
				k_script_context_town_in_combat,
				k_script_context_army,
				k_script_context_army_in_combat,
				k_script_context_ownable,
				k_script_context_garrison,
				k_script_context_garrison_in_combat,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}

	case k_script_action_give_creatures:
	case k_script_action_increase_cur_movement:
	case k_script_action_take_creatures:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_hero,
				k_script_context_hero_in_combat,
				k_script_context_town,
				k_script_context_town_in_combat,
				k_script_context_town_with_visitor,
				k_script_context_placed_event,
				k_script_context_seers_hut,
				k_script_context_quest_guard,
				k_script_context_quest_gate,
				k_script_context_army,
				k_script_context_army_in_combat,
				k_script_context_garrison,
				k_script_context_garrison_in_combat,
				k_script_context_garrison_with_visitor,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}

	case k_script_action_combat:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_hero,
				k_script_context_town,
				k_script_context_town_with_visitor,
				k_script_context_placed_event,
				k_script_context_seers_hut,
				k_script_context_quest_guard,
				k_script_context_quest_gate,
				k_script_context_army,
				k_script_context_garrison,
				k_script_context_garrison_with_visitor,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}

	case k_script_action_remove_this_adv_object:
		return context == k_script_context_placed_event;
	}

	return true;
}
